#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(dead_code)]
#![allow(unused)]
extern crate chrono;
use chrono::prelude::*;
use std::{fmt::format, process::exit};
/*
** Double any double-quote characters in a string.
** 将字符串中的双引号加倍
*/
pub fn escape(z: &str) -> String {
    let mut res = String::new();
    for ch in z.chars() {
        if ch == '"' {
            res.push(ch);
            res.push(ch);
        } else {
            res.push(ch);
        }
    }
    res
}
pub fn safe_malloc() {
    // useless in rust
    // deprecated
}
pub fn set_env(zVar: &str, zValue: &str) {
    if zValue.starts_with("() {") {
        std::env::set_var(zVar, "");
    } else {
        std::env::set_var(zVar, zValue);
    }
}
pub fn get_first_element(zInput: &str, zLeftOver: &str) -> Option<String> {
    // use split_whitespace instead!!!
    // deprecated
    None
}
pub fn str_dup(zSrc: &str) -> String {
    zSrc.to_string()
}
pub fn str_append(zPrior: &str, zSep: &str, zSrc: &str) -> String {
    if zSrc.is_empty() || zPrior.is_empty() {
        zSrc.to_string()
    } else {
        let mut res = String::from(zPrior);
        res += zSep;
        res += zSrc;
        res
    }
}
pub fn compare_etags(za: &str, zb: &str) -> bool {
    if za.is_empty() {
        return true;
    }
    if za.starts_with('"') {
        let zab = za.as_bytes();
        let zbb = zb.as_bytes();
        let lenb = zbb.len();
        if &zab[1..] == zbb && zab.len() > 1 + lenb && zab[1 + lenb] == '"' as u8 {
            return false;
        }
    }
    return za == zb;
}
pub fn remove_new_line(z: &str) {
    // use split_line instead
    // deprecated
}
pub fn Rfc822Date(t: i64) -> String {
    let mut nowtime = Utc.ymd(1970, 1, 1).and_hms_milli(0, 0, 0, 0);
    nowtime = nowtime + chrono::Duration::seconds(t);
    nowtime.to_rfc2822()
}
pub fn date_tag(zTag: &str, t: i64) -> i32 {
    let tmp = format!("{}: {}\r\n", zTag, Rfc822Date(t));
    print!("{}", tmp);
    tmp.len() as i32
}
pub fn parse_rfc2822_date(zDate: &str) -> i64 {
    let cur = DateTime::parse_from_rfc2822(zDate);
    if let Ok(res) = cur {
        res.timestamp()
    } else {
        0
    }
}
pub fn get_mime_type(zName: &str, nName: i32) -> &str {
    let aMime = [
        ("ai", 2, "application/postscript"),
        ("aif", 3, "audio/x-aiff"),
        ("aifc", 4, "audio/x-aiff"),
        ("aiff", 4, "audio/x-aiff"),
        ("arj", 3, "application/x-arj-compressed"),
        ("asc", 3, "text/plain"),
        ("asf", 3, "video/x-ms-asf"),
        ("asx", 3, "video/x-ms-asx"),
        ("au", 2, "audio/ulaw"),
        ("avi", 3, "video/x-msvideo"),
        ("bat", 3, "application/x-msdos-program"),
        ("bcpio", 5, "application/x-bcpio"),
        ("bin", 3, "application/octet-stream"),
        ("c", 1, "text/plain"),
        ("cc", 2, "text/plain"),
        ("ccad", 4, "application/clariscad"),
        ("cdf", 3, "application/x-netcdf"),
        ("class", 5, "application/octet-stream"),
        ("cod", 3, "application/vnd.rim.cod"),
        ("com", 3, "application/x-msdos-program"),
        ("cpio", 4, "application/x-cpio"),
        ("cpt", 3, "application/mac-compactpro"),
        ("csh", 3, "application/x-csh"),
        ("css", 3, "text/css"),
        ("dcr", 3, "application/x-director"),
        ("deb", 3, "application/x-debian-package"),
        ("dir", 3, "application/x-director"),
        ("dl", 2, "video/dl"),
        ("dms", 3, "application/octet-stream"),
        ("doc", 3, "application/msword"),
        ("drw", 3, "application/drafting"),
        ("dvi", 3, "application/x-dvi"),
        ("dwg", 3, "application/acad"),
        ("dxf", 3, "application/dxf"),
        ("dxr", 3, "application/x-director"),
        ("eps", 3, "application/postscript"),
        ("etx", 3, "text/x-setext"),
        ("exe", 3, "application/octet-stream"),
        ("ez", 2, "application/andrew-inset"),
        ("f", 1, "text/plain"),
        ("f90", 3, "text/plain"),
        ("fli", 3, "video/fli"),
        ("flv", 3, "video/flv"),
        ("gif", 3, "image/gif"),
        ("gl", 2, "video/gl"),
        ("gtar", 4, "application/x-gtar"),
        ("gz", 2, "application/x-gzip"),
        ("hdf", 3, "application/x-hdf"),
        ("hh", 2, "text/plain"),
        ("hqx", 3, "application/mac-binhex40"),
        ("h", 1, "text/plain"),
        ("htm", 3, "text/html; charset=utf-8"),
        ("html", 4, "text/html; charset=utf-8"),
        ("ice", 3, "x-conference/x-cooltalk"),
        ("ief", 3, "image/ief"),
        ("iges", 4, "model/iges"),
        ("igs", 3, "model/iges"),
        ("ips", 3, "application/x-ipscript"),
        ("ipx", 3, "application/x-ipix"),
        ("jad", 3, "text/vnd.sun.j2me.app-descriptor"),
        ("jar", 3, "application/java-archive"),
        ("jpeg", 4, "image/jpeg"),
        ("jpe", 3, "image/jpeg"),
        ("jpg", 3, "image/jpeg"),
        ("js", 2, "application/x-javascript"),
        ("kar", 3, "audio/midi"),
        ("latex", 5, "application/x-latex"),
        ("lha", 3, "application/octet-stream"),
        ("lsp", 3, "application/x-lisp"),
        ("lzh", 3, "application/octet-stream"),
        ("m", 1, "text/plain"),
        ("m3u", 3, "audio/x-mpegurl"),
        ("man", 3, "application/x-troff-man"),
        ("me", 2, "application/x-troff-me"),
        ("mesh", 4, "model/mesh"),
        ("mid", 3, "audio/midi"),
        ("midi", 4, "audio/midi"),
        ("mif", 3, "application/x-mif"),
        ("mime", 4, "www/mime"),
        ("movie", 5, "video/x-sgi-movie"),
        ("mov", 3, "video/quicktime"),
        ("mp2", 3, "audio/mpeg"),
        ("mp2", 3, "video/mpeg"),
        ("mp3", 3, "audio/mpeg"),
        ("mpeg", 4, "video/mpeg"),
        ("mpe", 3, "video/mpeg"),
        ("mpga", 4, "audio/mpeg"),
        ("mpg", 3, "video/mpeg"),
        ("ms", 2, "application/x-troff-ms"),
        ("msh", 3, "model/mesh"),
        ("nc", 2, "application/x-netcdf"),
        ("oda", 3, "application/oda"),
        ("ogg", 3, "application/ogg"),
        ("ogm", 3, "application/ogg"),
        ("pbm", 3, "image/x-portable-bitmap"),
        ("pdb", 3, "chemical/x-pdb"),
        ("pdf", 3, "application/pdf"),
        ("pgm", 3, "image/x-portable-graymap"),
        ("pgn", 3, "application/x-chess-pgn"),
        ("pgp", 3, "application/pgp"),
        ("pl", 2, "application/x-perl"),
        ("pm", 2, "application/x-perl"),
        ("png", 3, "image/png"),
        ("pnm", 3, "image/x-portable-anymap"),
        ("pot", 3, "application/mspowerpoint"),
        ("ppm", 3, "image/x-portable-pixmap"),
        ("pps", 3, "application/mspowerpoint"),
        ("ppt", 3, "application/mspowerpoint"),
        ("ppz", 3, "application/mspowerpoint"),
        ("pre", 3, "application/x-freelance"),
        ("prt", 3, "application/pro_eng"),
        ("ps", 2, "application/postscript"),
        ("qt", 2, "video/quicktime"),
        ("ra", 2, "audio/x-realaudio"),
        ("ram", 3, "audio/x-pn-realaudio"),
        ("rar", 3, "application/x-rar-compressed"),
        ("ras", 3, "image/cmu-raster"),
        ("ras", 3, "image/x-cmu-raster"),
        ("rgb", 3, "image/x-rgb"),
        ("rm", 2, "audio/x-pn-realaudio"),
        ("roff", 4, "application/x-troff"),
        ("rpm", 3, "audio/x-pn-realaudio-plugin"),
        ("rtf", 3, "application/rtf"),
        ("rtf", 3, "text/rtf"),
        ("rtx", 3, "text/richtext"),
        ("scm", 3, "application/x-lotusscreencam"),
        ("set", 3, "application/set"),
        ("sgml", 4, "text/sgml"),
        ("sgm", 3, "text/sgml"),
        ("sh", 2, "application/x-sh"),
        ("shar", 4, "application/x-shar"),
        ("silo", 4, "model/mesh"),
        ("sit", 3, "application/x-stuffit"),
        ("skd", 3, "application/x-koan"),
        ("skm", 3, "application/x-koan"),
        ("skp", 3, "application/x-koan"),
        ("skt", 3, "application/x-koan"),
        ("smi", 3, "application/smil"),
        ("smil", 4, "application/smil"),
        ("snd", 3, "audio/basic"),
        ("sol", 3, "application/solids"),
        ("spl", 3, "application/x-futuresplash"),
        ("src", 3, "application/x-wais-source"),
        ("step", 4, "application/STEP"),
        ("stl", 3, "application/SLA"),
        ("stp", 3, "application/STEP"),
        ("sv4cpio", 7, "application/x-sv4cpio"),
        ("sv4crc", 6, "application/x-sv4crc"),
        ("svg", 3, "image/svg+xml"),
        ("swf", 3, "application/x-shockwave-flash"),
        ("t", 1, "application/x-troff"),
        ("tar", 3, "application/x-tar"),
        ("tcl", 3, "application/x-tcl"),
        ("tex", 3, "application/x-tex"),
        ("texi", 4, "application/x-texinfo"),
        ("texinfo", 7, "application/x-texinfo"),
        ("tgz", 3, "application/x-tar-gz"),
        ("tiff", 4, "image/tiff"),
        ("tif", 3, "image/tiff"),
        ("tr", 2, "application/x-troff"),
        ("tsi", 3, "audio/TSP-audio"),
        ("tsp", 3, "application/dsptype"),
        ("tsv", 3, "text/tab-separated-values"),
        ("txt", 3, "text/plain"),
        ("unv", 3, "application/i-deas"),
        ("ustar", 5, "application/x-ustar"),
        ("vcd", 3, "application/x-cdlink"),
        ("vda", 3, "application/vda"),
        ("viv", 3, "video/vnd.vivo"),
        ("vivo", 4, "video/vnd.vivo"),
        ("vrml", 4, "model/vrml"),
        ("vsix", 4, "application/vsix"),
        ("wav", 3, "audio/x-wav"),
        ("wax", 3, "audio/x-ms-wax"),
        ("wiki", 4, "application/x-fossil-wiki"),
        ("wma", 3, "audio/x-ms-wma"),
        ("wmv", 3, "video/x-ms-wmv"),
        ("wmx", 3, "video/x-ms-wmx"),
        ("wrl", 3, "model/vrml"),
        ("wvx", 3, "video/x-ms-wvx"),
        ("xbm", 3, "image/x-xbitmap"),
        ("xlc", 3, "application/vnd.ms-excel"),
        ("xll", 3, "application/vnd.ms-excel"),
        ("xlm", 3, "application/vnd.ms-excel"),
        ("xls", 3, "application/vnd.ms-excel"),
        ("xlw", 3, "application/vnd.ms-excel"),
        ("xml", 3, "text/xml"),
        ("xpm", 3, "image/x-xpixmap"),
        ("xwd", 3, "image/x-xwindowdump"),
        ("xyz", 3, "chemical/x-pdb"),
        ("zip", 3, "application/zip"),
    ];
    use std::collections::HashMap;
    let mut aMimeMap = HashMap::new();
    for (a, b, c) in aMime {
        aMimeMap.insert(a.to_string(), c);
    }
    let pos = zName.rfind('0').unwrap_or(0);
    let z = &zName[pos..];
    let len = z.len();
    let mut zSuffix = String::new();
    if len < 19 {
        zSuffix = z.to_string().to_lowercase();
        if let Some(&res) = aMimeMap.get(&zSuffix) {
            return res;
        }
    }
    "application/octet-stream"
}
/// Allowed characters:  0-9a-zA-Z,-./:_~
pub fn allowed_in_name(i: u8) -> bool {
    44 <= i && i <= 59 ||
    65 <= i && i <= 90 ||
    i == 95 ||
    97 <= i && i <= 122 ||
    i == 126 
}
pub fn sanitize_string(z: &mut String) -> i32 {
    let mut nChange = 0;
    unsafe {
        let v = z.as_mut_vec();
        let mut pos = 0;
        while (pos < v.len()) {
            if !allowed_in_name(v[pos]) {
                if (v[pos] == '%' as u8 && pos < v.len() - 2) {
                    v.remove(pos + 1);
                    v.remove(pos + 1);
                }
                v[pos] = '_' as u8;
                nChange += 1;
            }
            pos += 1;
        }
    }
    nChange
}
pub fn count_slashed(z: &str) -> i32 {
    z.chars().fold(0, |acc, x| acc + (x == '/') as i32)
}




#[derive(Default, Debug)]
struct RunTime {
    z_root: String,
    z_tmp_nam: String,
    z_protocol: String,
    z_method: String,
    z_script: String,
    z_real_script: String,
    z_home: String,
    z_query_string: String,
    z_file: String,
    z_dir: String,
    z_path_info: String,
    z_agent: String,
    z_server_name: String,
    z_server_port: String,
    z_cookie: String,
    z_http_host: String,
    z_real_port: String,
    z_remote_addr: String,
    z_referer: String,
    z_accept: String,
    z_accept_encoding: String,
    z_content_length: String,
    z_content_type: String,
    z_query_suffix: String,
    z_auth_type: String,
    z_auth_arg: String,
    z_remote_user: String,
    z_if_none_match: String,
    z_if_modified_since: String,
    n_in: i32,
    n_out: i32,
    z_reply_status: String,
    status_sent: bool,
    z_log_file: String,
    debug_flag: bool,
    begin_time: i64,
    close_connection: bool,
    n_requests: i32,
    omit_log: bool,
    use_https: bool,
    z_http: String,
    use_timeout: bool,
    use_standalone: bool,
    ipv6_only: bool,
    ipv4_only: bool,
    // rusage 结构体未定义
    mx_age: i32,
    default_path: String,
    z_scgi: String,
    range_start: i64,
    range_end: i64,
    max_cpu: i32,
}
impl RunTime {
    pub fn new() -> RunTime {
        RunTime {
            z_http: "http".to_string(),
            use_timeout: true,
            mx_age: 120,
            default_path: "/bin:/usr/bin".to_string(),
            ..Default::default()
        }
    }
    pub fn pc(&mut self, tmp: String) {
        self.n_out += tmp.len() as i32;
        print!("{}", tmp);
    }
    pub fn make_log_entry(&mut self, exitCode: i32, linenum: i32) {}
    pub fn start_response(&mut self, zResultCode: &str) {
        let timenow = Utc::now().timestamp();
        if self.status_sent {
            return;
        }
        let tmp = format!("{} {}\r\n", self.z_protocol, zResultCode);
        print!("{}", tmp);
        self.n_out += tmp.len() as i32;
        self.z_reply_status = zResultCode[..3].to_string();
        if self.z_reply_status.chars().nth(0).unwrap() >= '4' {
            self.close_connection = true;
        }
        if self.close_connection {
            let tmp = format!("Connection: close\r\n");
            self.pc(tmp);
        } else {
            let tmp = format!("Connection: keep-alive\r\n");
            self.pc(tmp);
        }
        self.n_out += date_tag("Date", timenow);
    }
    pub fn not_found(&mut self, lineno: i32) {
        self.start_response("404 Not Found");
        self.pc(format!(
            "Content-type: text/html; charset=utf-8\r\n\
            \r\n\
            <head><title lineno=\"{}\">Not Found</title></head>\n\
            <body><h1>Document Not Found</h1>\n\
            The document {} is not available on this server\n\
            </body>\n",
            lineno, self.z_script
        ));
        self.make_log_entry(0, lineno);
        std::process::exit(0);
    }
    pub fn forbidden(&mut self, lineno: i32) {
        self.start_response("403 Forbidden");
        self.pc(format!(
            "Content-type: text/plain; charset=utf-8\r\n\
            \r\n\
            Access denied\n"
        ));
        self.close_connection = true;
        self.make_log_entry(0, lineno);
        std::process::exit(0);
    }
    pub fn not_authorized(&mut self, zRealm: String) {
        self.start_response("401 Authorization Required");
        self.pc(format!(
            "WWW-Authenticate: Basic realm=\"{}\"\r\n\
            Content-type: text/html; charset=utf-8\r\n\
            \r\n\
            <head><title>Not Authorized</title></head>\n\
            <body><h1>401 Not Authorized</h1>\n\
            A login and password are required for this document\n\
            </body>\n",
            zRealm
        ));
        self.make_log_entry(0, 110);
    }
    pub fn cgi_error(&mut self) {
        self.start_response("500 Error");
        self.pc(format!(
            "Content-type: text/html; charset=utf-8\r\n\
            \r\n\
            <head><title>CGI Program Error</title></head>\n\
            <body><h1>CGI Program Error</h1>\n\
            The CGI program {} generated an error\n\
            </body>\n",
            self.z_script
        ));
        self.make_log_entry(0, 120);
        std::process::exit(0);
        exit(0);
    }
    pub fn timeout(&mut self, iSig: u8) {
        if !self.debug_flag {
            if !self.z_script.is_empty() {
                let mut zBuf = ['9' as u8, '0' as u8, '0' as u8];
                zBuf[1] += (iSig / 10) % 10;
                zBuf[2] += iSig % 10;
                self.z_reply_status = zBuf.iter().map(|&c| c as char).collect();
                self.make_log_entry(0, 130);
            }
        }
    }
    pub fn cgi_script_writable(&mut self) {
        self.start_response("500 CGI Configuration Error");
        self.pc(format!(
            "Content-type: text/plain; charset=utf-8\r\n\
            \r\n\
            The CGI program {} is writable by users\
             other than its owner.\n",self.z_real_script));
        self.make_log_entry(0, 140);
        exit(0);
    }
    pub fn malfunction(&mut self, linenum: i32, zFormat: &str, args: &[&str]) {
        self.start_response("500 Server Malfunction");
        self.pc(format!(
            "Content-type: text/plain; charset=utf-8\r\n\
            \r\n\
            Web server malfunctioned; error number {}\n\n", linenum
        ));
        self.make_log_entry(0, linenum);
        exit(0);
    }
    pub fn redirect(&mut self, zPath: &str, iStatus: i32, finish: bool, lineno: i32) {
        match iStatus {
            301 => self.start_response("301 Permanent Redirect"),
            300 => self.start_response("308 Permanent Redirect"),
            _ => self.start_response("302 Temporary Redirect"),
            
        }
        if self.z_server_port.is_empty() || self.z_server_port == "80".to_string() {
            self.pc(format!(
                "Location: {}://{}{}{}\r\n", 
                self.z_http, self.z_server_name, 
                zPath, self.z_query_suffix
            ));
        } else {
            self.pc(format!(
                "Location: {}://{}:{}{}{}\r\n", 
                self.z_http, self.z_server_name, self.z_server_port,
                zPath, self.z_query_suffix
            ));
        }
        if finish {
            self.pc(format!("Content-length: 0\r\n"));
            self.pc(format!("\r\n"));
            self.make_log_entry(0, lineno);
        }
        use std::io::Write;
        std::io::stdout().flush();
    }
    pub fn check_basic_authorization(&mut self, zAuthFile: &str) -> bool {
        // todo 
        true
    }
    


}
pub fn decode64(z64: &str) {
    // decode the base64 string;
    // use base64::decode() instead
    // deprecated
}


pub fn all_test() {
    assert_eq!(&escape("a\"b\"c"), "a\"\"b\"\"c");
    set_env("ALTHTTPD_TEST", "TRUE");
    assert_eq!(std::env::var("ALTHTTPD_TEST"), Ok("TRUE".to_string()));
    assert_eq!(str_append("A", " ", "B"), "A B".to_string());
    assert_eq!(str_append("", "abc", "B"), "B".to_string());
    assert_eq!(compare_etags("ab", "ba"), false);
    assert_eq!(compare_etags("ab", "\"ab\""), false);
    assert_eq!(compare_etags("\"ab", "\"ab"), true);
    assert_eq!(compare_etags("\"ab\"", "\"ab"), false);
    // test of parse and unparse
    let mut t1 = 0;
    while t1 < 0x7ffff {
        let t2 = parse_rfc2822_date(&Rfc822Date(t1));
        assert_eq!(t1, t2);
        t1 += 127;
    }
    println!("[INFO] ALL CHECK HAS BEEN SUCCESSFULLY DONE!");
}
pub fn main() {
    let runtime = RunTime::new();
    println!("{:?}", runtime);
    all_test();
}
